//
//  FFTabViewE.swift
//  ZgwApp
//
//  Created by li on 2025-02-10.
//

import SwiftUI
import Combine

struct FFPageViewTabKey: @preconcurrency EnvironmentKey {
	@MainActor static let defaultValue: AnyView = AnyView(EmptyView())
	
	typealias Value = AnyView
}

enum FFPageViewTabPosition {
	case top, bottom
}

struct FFPageViewTabPositionKey: EnvironmentKey {
	static let defaultValue: FFPageViewTabPosition = .top
}

struct FFPageViewScrollOffsetKey: @preconcurrency EnvironmentKey {
	
	@MainActor static let defaultValue: CGFloat = 0
}

struct FFPageTabViewHorizontalAlginmentKey: EnvironmentKey {
	
	static let defaultValue: HorizontalAlignment = .leading
}

struct FFPageTabViewHorizontalSpacingKey: EnvironmentKey {
	
	nonisolated(unsafe) static var defaultValue: CGFloat = 0
}

struct FFScrollViewPagingEnabledKey: @preconcurrency EnvironmentKey {
	@MainActor static var defaultValue: Bool = false
}

struct FFScrollViewPhaseChangeKey: @preconcurrency EnvironmentKey {
	
	@MainActor static let defaultValue: FFScrollPhaseChangeCallback = {_,_,_ in }
}

typealias FFScrollPhaseChangeCallback = (_ oldValue:  FFScrollPhase, _ newValue: FFScrollPhase, _ context: FFScrollPhaseChangeContext) -> Void

struct FFScrollGeometryChangeKey: @preconcurrency EnvironmentKey {
	@MainActor static var defaultValue: FFScrollGeometryChangeCallback = { _ in }
}

typealias FFScrollGeometryChangeCallback = (_ scrollGeomtery: FFScrollGeometry) -> Void

//struct FFScrollContentOffsetKey: EnvironmentKey {
//	static var defaultValue: CGPoint = .zero
//}

struct FFScrollPositionKey: EnvironmentKey {
	
	nonisolated(unsafe) static var defaultValue: FFScrollPosition = .init()
}

struct FFScrollPositionOffsetKey: @preconcurrency EnvironmentKey {
	@MainActor static var defaultValue: FFScrollPoistionCallback = {_  in}
}

typealias FFScrollPoistionCallback = (CGPoint) -> Void


extension EnvironmentValues {
	/**
	 FFPageView 默认选项卡样式
	 */
	var fFPageViewTab: AnyView {
		set {
			self[FFPageViewTabKey.self] = newValue
		}
		
		get {
			self[FFPageViewTabKey.self]
		}
	}
	/**
	 FFPageView 选项卡的位置
	 */
	var fFPageViewTabPosition: FFPageViewTabPosition {
		set {
			self[FFPageViewTabPositionKey.self] = newValue
		}
		
		get {
			self[FFPageViewTabPositionKey.self]
		}
	}
	
	/**
	 FFPageView page 滚动视图的偏移量
	 */
	var fFPageViewScrollContentOffet: CGFloat {
		set {
			self[FFPageViewScrollOffsetKey.self] = newValue
		}
		
		get {
			self[FFPageViewScrollOffsetKey.self]
		}
	}
	
	var fFPageTabViewHorizontalAlginment: HorizontalAlignment {
		set {
			self[FFPageTabViewHorizontalAlginmentKey.self]  = newValue
		}
		
		get {
			self[FFPageTabViewHorizontalAlginmentKey.self]
		}
	}	
	var fFPageTabViewHorizontalSpacing: CGFloat {
		set {
			self[FFPageTabViewHorizontalSpacingKey.self]  = newValue
		}
		
		get {
			self[FFPageTabViewHorizontalSpacingKey.self]
		}
	}
	
	var ffScrollViewPagingEnabled: Bool {
		set {
			
			let oldValue = self[FFScrollViewPagingEnabledKey.self]
			if newValue != oldValue {
				self[FFScrollViewPagingEnabledKey.self] = newValue
			}
			
		}
		
		get {
			self[FFScrollViewPagingEnabledKey.self]
		}
	}
	
	var ffScrollViewPhaseChange: FFScrollPhaseChangeCallback {
		set {
			self[FFScrollViewPhaseChangeKey.self] = newValue
		}
		
		get {
			self[FFScrollViewPhaseChangeKey.self]
		}
	}
	
	var ffScrollGeometryChange: FFScrollGeometryChangeCallback {
		set {
			self[FFScrollGeometryChangeKey.self] = newValue

		}
		
		get {
			self[FFScrollGeometryChangeKey.self]
		}
	}
	
	var ffScrollPosition: FFScrollPosition  {
		set {
			self[FFScrollPositionKey.self] = newValue
		}
		
		
		get {
			self[FFScrollPositionKey.self]
		}
	}

	var ffScrollPositionOffset: FFScrollPoistionCallback  {
		set {
			self[FFScrollPositionOffsetKey.self] = newValue
		}
		
		
		get {
			self[FFScrollPositionOffsetKey.self]
		}
	}

}

extension View {
	func ffPageViewTab<T: View>(position: FFPageViewTabPosition,  @ViewBuilder tabView: () -> T) -> some View {
		self
			.environment(\.fFPageViewTabPosition, position)
			.environment(\.fFPageViewTab, AnyView(tabView()))
	}
	
	func ffPageViewTab<D: RandomAccessCollection, C: View>(position: FFPageViewTabPosition, tabView: () -> FFPageTabView<D, C>) -> some View where D.Element: Hashable {
		
		self
			.environment(\.fFPageViewTabPosition, position)
			.environment(\.fFPageViewTab, AnyView(tabView()))
	}
	
	func ffPageTabView(alignment horizontal: HorizontalAlignment) -> some View {
		self
			.environment(\.fFPageTabViewHorizontalAlginment, horizontal)
	}
	
	func ffPageTabView(spacing horizontal: CGFloat) -> some View {
		self
			.environment(\.fFPageTabViewHorizontalSpacing, horizontal)
	}
	
	func ffScrollViewPagingEnabled(_ enabled: Bool) -> some View {
		environment(\.ffScrollViewPagingEnabled, enabled)
	}
	
	func onFFScrollPhaseChange(_ action: @escaping FFScrollPhaseChangeCallback) -> some View {
		environment(\.ffScrollViewPhaseChange, action)
	}
	

	func onFFScrollGeometryChange<T>(for type: T.Type, of transform: @escaping (_ geomtery: FFScrollGeometry) -> T, action: @escaping (_ oldValue: T, _ newValue: T) -> Void) -> some View where T : Equatable {
		
		modifier(FFScrollGeometryChangeModifier(transform: transform, action: action))
	}
	
	func ffScrollPosition(position: Binding<FFScrollPosition>) -> some View {
		modifier(FFScrollPositionModifier(position: position))
	}
	
	
	func ffHidden(value: Bool = true) -> some View {
	
		modifier(FFViewHiddenModifier(hidden: value))
	}
	
	public func onFFChange<V>(of value: V, initial: Bool = false, _ action: @escaping (_ oldValue: V, _ newValue: V) -> Void) -> some View where V : Equatable {
		
		modifier(FFChangeViewModifier(data: value, initial: initial, action1: nil, action2: action))
			
	}
}


private struct FFScrollGeometryChangeModifier<T: Equatable>:  ViewModifier {
	@State private var storage: ValueStorage

	init(transform: @escaping (FFScrollGeometry) -> T, action: @escaping (T) -> Void) {
		self.storage = ValueStorage(transform: transform, action1: action, action2: nil)

	}
	
	init(transform: @escaping (FFScrollGeometry) -> T, action: @escaping (T,T) -> Void) {
		self.storage = ValueStorage(transform: transform, action1: nil, action2: action)
	}
	
	func body(content: Content) -> some View {
		content
			.environment(\.ffScrollGeometryChange, {
				proxy in
				storage.onChanged(proxy: proxy)
			})
	}
	
	private class ValueStorage {
		
		private var oldValue: T?
		private var newValue: T?
		private let transform: (FFScrollGeometry)  -> T
		private let action1: ((T) -> Void)?
		private let action2: ((T,T) -> Void)?
		
		
		init(transform: @escaping (FFScrollGeometry) -> T, action1: ((T) -> Void)?, action2: ((T, T) -> Void)?) {
			
			self.transform = transform
			self.action1 = action1
			self.action2 = action2
	
		}
		
//		mutating func onChanged(proxy: FFScrollGeometry)  {
		func onChanged(proxy: FFScrollGeometry)  {
			let value =  transform(proxy)
			if oldValue == nil {
				oldValue =  value
				newValue =  value
			}else {
				oldValue = newValue
				newValue = value
			}
			
			if  let action1, let newValue = newValue {
				action1(newValue)
			}
			
			if let action2, let oldValue, let newValue {
				action2(oldValue, newValue)
			}
		}
	}
}

private struct FFScrollPositionModifier: ViewModifier  {
	
	init(position: Binding<FFScrollPosition>) {
//		self.value = ValueStorage(position: position.wrappedValue)
		self.position = position
	}
	
	var position: Binding<FFScrollPosition>
//	@State var value: ValueStorage
	func body(content: Content) -> some View {
		content
//			.environment(\.ffScrollPositionOffset
//						  , {
//				point in
//				var position = FFScrollPosition(.zero)
//				position.contentOffset = point
//				self.position.wrappedValue = position
//			})
//			.environment(\.ffScrollPosition, position.wrappedValue)
//			.environmentObject(value)
	}
	
	class ValueStorage: ObservableObject {
		
		init(position: FFScrollPosition) {
			self._position = .init(wrappedValue: position)
		}
		
		@State var position: FFScrollPosition
	}
}

private struct FFViewHiddenModifier: ViewModifier {
	let hidden: Bool
	func body(content: Content) -> some View {
	
		if hidden {
			content.hidden()
		}else {
			content
		}
	}
}

private struct FFChangeViewModifier<T: Equatable>: ViewModifier {
	
	let data: T
	init(data: T, initial: Bool, action1: ((T) -> Void)?, action2: ((T,T) -> Void)?) {
		self.data = data
		self.initial = initial
		self.storage = ValueStorage(action1: action1, action2: action2)
	}
	
	@State private var storage: ValueStorage
	@State var initial: Bool
	@State var hasInitial: Bool = false
	
	func body(content: Content) -> some View {
		content
			.onReceive(Just(data)) { value in
				if hasInitial {
					storage.onChanged(value: value)
				}else {
					if initial {
						
						storage.onChanged(value: value)
					}
					hasInitial = true
				}
			}
	}
	
	private class ValueStorage {
		
		
		private var oldValue: T?
		private var newValue: T?
		private let action1: ((T) -> Void)?
		private let action2: ((T,T) -> Void)?
		
		init(action1: ((T) -> Void)?, action2: ((T,T) -> Void)?) {
			self.action1 = action1
			self.action2 = action2
		}

		func onChanged(value: T)  {
			
			if oldValue == nil {
				oldValue =  value
				newValue =  value
			}else {
				oldValue = newValue
				newValue = value
			}
			if  let action1, let newValue = newValue {
				action1(newValue)
			}
			
			if let action2, let oldValue, let newValue {
				action2(oldValue, newValue)
			}
		}
	}
}
